/**
 * Licensed to Jasig under one or more contributor license agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership. Jasig licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy
 * of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.baijia.commons.validation;

import com.baijia.commons.authentication.Assertion;
import com.baijia.commons.authentication.BaseResponse;
import com.baijia.commons.dession.cache.SessionCache;
import com.baijia.commons.session.AbstractPassportFilter;
import com.baijia.commons.util.PPCommonUtils;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * @title PassportTicketValidationFilter
 * @desc 验证ST 成功跳转最初访问的url
 * @author caoliang
 * @date 2015年12月1日
 * @version 1.0
 */
public class PassportTicketValidationFilter extends AbstractPassportFilter {

    private TicketValidator ticketValidator;

    /** 为true时，登录成功后，将跳转配置的url，而不是用户最初请求的url */
    private boolean redirectAfterValidation = false;

    /** 验证失败时，是否抛出异常 */
    private boolean exceptionOnValidationFailure = true;

    /** 是否使用共享session */
    private boolean useSession = true;

    private SessionCache sessionCache;

    private String cookieMaxAge;

    protected TicketValidator getTicketValidator(final FilterConfig filterConfig) {
        return this.ticketValidator;
    }

    @Override
    protected void initInternal(final FilterConfig filterConfig) throws ServletException {
        setExceptionOnValidationFailure(parseBoolean(getPropertyFromInitParams(filterConfig,
            "exceptionOnValidationFailure", "true")));
        logger.trace("Setting exceptionOnValidationFailure parameter: " + this.exceptionOnValidationFailure);

        setRedirectAfterValidation(parseBoolean(getPropertyFromInitParams(filterConfig, "redirectAfterValidation",
            "true")));
        logger.trace("Setting redirectAfterValidation parameter: " + this.redirectAfterValidation);

        setUseSession(parseBoolean(getPropertyFromInitParams(filterConfig, "useSession", "true")));
        logger.trace("Setting useSession parameter: " + this.useSession);

        setTicketValidator(new PassportTicketValidator(passportConfig.getServerValidate(), passportConfig.getAppId()));
        ServletContext context = filterConfig.getServletContext();
        WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(context);
        sessionCache = applicationContext.getBean(SessionCache.class);
        setCookieMaxAge(passportConfig.getCookieMaxAge());
        super.initInternal(filterConfig);
    }

    @Override
    public void init() {
        super.init();
        PPCommonUtils.assertNotNull(this.ticketValidator, "ticketValidator cannot be null.");
    }

    protected boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
        final FilterChain filterChain) throws IOException, ServletException {
        return true;
    }

    /**
     * 验证ST成功后，记录共享Session，以及在session存登录信息
     *
     * @param request the HttpServletRequest.
     * @param response the HttpServletResponse.
     * @param assertion the successful Assertion from the server.
     */
    protected void onSuccessfulValidation(final HttpServletRequest request, final HttpServletResponse response,
        final Assertion assertion) {
        // 存储<ST,session>，用于退出登录
        String ST = PPCommonUtils.safeGetParameter(request, getArtifactParameterName());
        HttpSession session = request.getSession();
        sessionCache.set(ST, session);
        // 共享session要有登录信息
        if (this.useSession) {
            session.setAttribute(CONST_PASSPORT_ASSERTION, assertion);
        }

        logger.info("successfulValidation cookieMaxAge:{}", this.cookieMaxAge);
        // 如果passport.properties中有配置cookie的有效期,则修改cookie有效期
        // if(StringUtils.isNotBlank(this.cookieMaxAge) && StringUtils.isNumeric(this.cookieMaxAge)) {
        // int maxAge = Integer.valueOf(cookieMaxAge);
        //
        // Cookie cookie = org.springframework.web.util.WebUtils.getCookie(
        // request, CONST_PASSPORT_ID);
        // if(cookie != null) {
        // String path = request.getHeader("Origin");
        // logger.info("successfulValidation origin:{}", path);
        // // Cookie cookieNew = null;
        // // if(path != null) {
        // // if(path.matches("^http(s)?://(.+\\.)?(baijiahulian\\.com)$")) {
        // // cookieNew = createCookie(CONST_PASSPORT_ID, cookie.getValue(), Constant.BAIJIAHULIAN_DOMAIN);
        // // }else if(path.matches("^http(s)?://(.+\\.)?(genshuixue\\.com)$")) {
        // // cookieNew = createCookie(CONST_PASSPORT_ID, cookie.getValue(), Constant.GENSHUIXUE_DOMAIN);
        // // }
        // // }
        // Cookie cookieBJHL = createCookie(CONST_PASSPORT_ID, cookie.getValue(), Constant.BAIJIAHULIAN_DOMAIN);
        // Cookie cookieGSX = createCookie(CONST_PASSPORT_ID, cookie.getValue(), Constant.GENSHUIXUE_DOMAIN);
        // cookie.setMaxAge(0);
        // cookieBJHL.setMaxAge(maxAge);
        // cookieGSX.setMaxAge(maxAge);
        // response.addCookie(cookie);
        // response.addCookie(cookieBJHL);
        // response.addCookie(cookieGSX);
        // }
        //
        // }
    }

    /**
     * Template method that gets executed if validation fails. This method is called right after the exception is caught
     * from the ticket validator but before any of the processing of the exception occurs.
     *
     * @param request the HttpServletRequest.
     * @param response the HttpServletResponse.
     */
    protected void onFailedValidation(final HttpServletRequest request, final HttpServletResponse response) {
        // do nothing
    }

    @Override
    public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
        final FilterChain filterChain) throws IOException, ServletException {
        if (!preFilter(servletRequest, servletResponse, filterChain)) {
            return;
        }

        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;
        final String ticket = PPCommonUtils.safeGetParameter(request, getArtifactParameterName());

        // 有pp_ticket参数，如果还未登录再去验证ticket，否则就是已经登录，使用ajax的方式，没有更新URL
        if (PPCommonUtils.isNotBlank(ticket) && !isUserLogin(request, response)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Attempting to validate ticket: " + ticket);
            }

            try {
                final BaseResponse baseResponse =
                    this.ticketValidator.validate(ticket, constructServiceUrl(request, response));
                if (baseResponse.getStatus() != BaseResponse.OK) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("|Passport| server validate ticket wrong! response:" + baseResponse);
                    }
                    response.sendRedirect(passportConfig.getClientLoginAddr() + "?errCode=" + baseResponse.getStatus());
                    return;
                }
                Assertion assertion = baseResponse.getAssertion();
                logger.info("|Passport|Successfully authenticated user: "
                    + assertion.getChainedAuthentications().get(0).getPrincipal().getId());
                // 参数暂时不放用户验证信息
                // request.setAttribute(CONST_CAS_ASSERTION, assertion);

                onSuccessfulValidation(request, response, assertion);
                // 这里可扩展配置，成功登录后，跳转业务系统指定页面，而不是登陆前想访问的页面
                if (this.redirectAfterValidation) {
                    logger.debug("Redirecting after successful ticket validation.");
                    response.sendRedirect(constructServiceUrl(request, response));
                    return;
                }
            } catch (final TicketValidationException e) {
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                logger.error("validate pp_ticket error", e);

                onFailedValidation(request, response);

                if (this.exceptionOnValidationFailure) {
                    throw new ServletException(e);
                }
                if (passportConfig.getRedirect() == null || passportConfig.getRedirect() == true) {
                    response.sendRedirect(passportConfig.getClientLoginAddr());
                } else {
                    needLoginResponse(request, response);
                }
                return;
            }
        }

        filterChain.doFilter(request, response);

    }

    public final void setTicketValidator(final TicketValidator ticketValidator) {
        this.ticketValidator = ticketValidator;
    }

    public final void setRedirectAfterValidation(final boolean redirectAfterValidation) {
        this.redirectAfterValidation = redirectAfterValidation;
    }

    public final void setExceptionOnValidationFailure(final boolean exceptionOnValidationFailure) {
        this.exceptionOnValidationFailure = exceptionOnValidationFailure;
    }

    public final void setUseSession(final boolean useSession) {
        this.useSession = useSession;
    }

    public void setCookieMaxAge(String cookieMaxAge) {
        this.cookieMaxAge = cookieMaxAge;
    }

    private Cookie createCookie(String name, String cookieValue, String domain) {
        Cookie cookie = new Cookie(name, cookieValue);
        if (org.apache.commons.lang.StringUtils.isNotBlank(domain)) {
            cookie.setDomain(domain);
        }
        return cookie;
    }
}
